﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using Microsoft.VisualBasic;
using modBusTest;
using Modbus.Device;
using System.Data.SQLite;
using System.Data.Common;
namespace ModbusTestTool
{
    public partial class ParamWindows : Form
    {
        /// <summary>
        /// An internal datastructure that store all the modbus values
        /// </summary>
        public modbusmap internalModbusMap;
        /// <summary>
        /// A list of all error accumulated by this window
        /// </summary>
        public List<string> errorLog = new List<string>();
        
        /// <summary>
        /// A timer class to do the polling by default set to 2 seconds will be updated
        /// so it isn't two seconds
        /// </summary>
        System.Timers.Timer update = new System.Timers.Timer(2000);
        /// <summary>
        /// If continous is et to false the timer only runs once
        /// </summary>
        public bool continuous {set;get;}
        /// <summary>
        /// If logging is enable it will will store data in this file
        /// </summary>
        StreamWriter outFileHandle;
        /// <summary>
        /// name of logfile
        /// </summary>
        string logFileName;
        /// <summary>
        /// Set the internal timer and tells it how long to wait between polls
        /// </summary>
        public double updateTimetime
        {
            set
            {
                update.Interval = value;
            }
            get
            {
                return update.Interval;
            }
        }

        /// <summary>
        ///a lock to protect the when the error list being read 
        /// </summary>
        private object lockVariable = new object();
        /// <summary>
        /// Variable to set whether we log or not
        /// </summary>
        public bool logging {set;get;}

        /// <summary>
        /// This when closing the form wait till me are out of the timer loop
        /// </summary>
        private object inTimerLoop = new object();
        



        public ParamWindows()
        {
            InitializeComponent();
            update.Elapsed += new System.Timers.ElapsedEventHandler(update_Tick);
            continuous = false;
            this.SizeChanged += new EventHandler(ParamWindows_SizeChanged);
            this.FormClosing += new FormClosingEventHandler(ParamWindows_FormClosing);
            dataGridView1.CellDoubleClick += new DataGridViewCellEventHandler(dataGridView1_CellDoubleClick);

        }


        SqlCommunication sqlHandle;

        public ParamWindows(byte slaveID, ModbusSerialMaster port, string inputFileName, string inlogFileName, int pollRate)
        {
            
            InitializeComponent();
            dataGridView1.AutoGenerateColumns = false;
            update.Elapsed += new System.Timers.ElapsedEventHandler(update_Tick);
            continuous = false;
            this.SizeChanged += new EventHandler(ParamWindows_SizeChanged);
            this.FormClosing += new FormClosingEventHandler(ParamWindows_FormClosing);
            dataGridView1.CellDoubleClick += new DataGridViewCellEventHandler(dataGridView1_CellDoubleClick);
            logFileName = inlogFileName;
            setFileName(inputFileName);
            internalModbusMap.master = port;
            internalModbusMap.slaveID = slaveID;
            update.Interval = pollRate;
            
            //setting up data gridview properties
            dataGridView1.DataSource = internalModbusMap.storage;
            ParamName.DataPropertyName = "paramName";
            RegisterNumber.DataPropertyName = "register";
            ParamValue.DataPropertyName = "value";


            sqlHandle = new SqlCommunication(inputFileName, internalModbusMap);
            //HandleCreatingSqlTables(inputFileName);
        }

        

        
        
 
        /// <summary>
        /// When a cell is double clicked we ask user to set new value which is written into device
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void dataGridView1_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
        {
            string value = Microsoft.VisualBasic.Interaction.InputBox("Enter New Value", internalModbusMap.storage[e.RowIndex].paramName, internalModbusMap.storage[e.RowIndex].value, 200, 100);

            if (value != null && value != "")
            {
               double temp;
               if (double.TryParse(value, out temp))
               {
                   internalModbusMap.storage[e.RowIndex].value = value;
               }
               else
               {
                   MessageBox.Show("Invalid Value");
               }
            }

            try
            {
                internalModbusMap.writeDirty();
                if (update.Interval > 60000)
                {
                    internalModbusMap.getDataFromDevice();
                }
            }
            catch (Exception err)
            {
                errorLog.Add(err.Message);
                
            }
        }




        /// <summary>
        /// When the form is being closed stop the timer.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>        
        void ParamWindows_FormClosing(object sender, FormClosingEventArgs e)
        {
            update.Stop();
            if (logging)
            {
                ((IDisposable)outFileHandle).Dispose();
            }
        }


        /// <summary>
        /// If some one resizes the window the window will resize internal state
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void ParamWindows_SizeChanged(object sender, EventArgs e)
        {
            dataGridView1.Size = new Size(this.Size.Width - 32, this.Size.Height - 85);
        }


        
        /// <summary>
        /// Writes all dirty pages to modbus registers 
        /// and updates the rest of the pages
        /// </summary>
        void updateModbusData()
        {
            try
            {
                //internalModbusMap.writeDirty();
                internalModbusMap.getDataFromDevice();
            }
            catch (Exception err)
            {
                lock (lockVariable)
                {
                    errorLog.Add(err.Message);
                }
            }
            
        }


        /// <summary>
        /// Returns the errors occured during modbus enumeration
        /// </summary>
        /// <returns>A list strings that represents errors encountered
        /// during modbus polling</returns>
        public string [] error()
        {

            if (Monitor.TryEnter(lockVariable))
            {
                try
                {
                    if (errorLog.Count > 0)
                    {
                        List<string> tempErrList = new List<string>();

                        foreach (string item in errorLog)
                        {
                            tempErrList.Add(item);
                        }
                        errorLog.Clear();
                        return tempErrList.ToArray();
                    }
                    else if (errorLog.Count == 0)
                    {
                        return null;
                    }
                }
                finally
                {
                    Monitor.Exit(lockVariable);
                }
            }

            return null;
        }
        
        void update_Tick(object sender, EventArgs e)
        {

            update.Stop();
            //update the modbus data in local varaible from device
            updateModbusData();
            
            //should log data into database.
            if (logbox.Checked)
            {
                var items = from n in internalModbusMap.storage select n.value;

                sqlHandle.AddRow(items);
            }

            //don't need this as we will be writing to the data base  
            
            //outFileHandle.Write('\t');
            //if (logging)
            //{
            //    outFileHandle.Write(DateTime.Now.ToString());
            //    outFileHandle.Write('\t');
            //}
            //for (int i = 0; i < internalModbusMap.getList().Count; i++)
            //{
            //    //removing since this should be handled by data bindings
            //    //dataGridView1[1, i].Value = internalModbusMap.getList()[i].value;
            //    if (logging)
            //    {
            //        outFileHandle.Write(internalModbusMap.getList()[i].value);
            //        outFileHandle.Write('\t');
            //    }
            //}

            //if (logging)
            //{
            //    outFileHandle.WriteLine();
            //}
            
            //dataGridView1.Update();
            //this.Invoke((MethodInvoker)delegate { this.dataGridView1.Update(); });
            update.Start();

            if (!continuous)
            {
                update.Stop();
            }
                
        }

        /// <summary>
        /// Function is used to add line to sql data base
        /// </summary>
        /// <param name="items"></param>

        AutoCompleteStringCollection paramNames = new AutoCompleteStringCollection();
        //List<string> paramNames = new List<string>();
        
        /// <summary>
        /// Using this function you set the local file name for the registers the parameter window will read
        /// </summary>
        /// <param name="inLink"></param>
        public void setFileName(string inFileName)
        {
            
           internalModbusMap = new modbusmap();
           
           try
           {
               internalModbusMap.readDataXml(inFileName);
           }
           catch (Exception err)
           {

               errorLog.Add(err.Message);
           }
            
            
            foreach (BaseRegister item in internalModbusMap.storage)
            {

                //Removing this since this should be handled by databindings
                //dataGridView1.Rows.Add(item.paramName, item.value);
                paramNames.Add(item.paramName);
            }

            textBox1.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
            textBox1.AutoCompleteSource = AutoCompleteSource.CustomSource;
            textBox1.AutoCompleteCustomSource = paramNames;
           
            //wrting headers
            if (logging)
            {
                outFileHandle = new StreamWriter("OutFile.txt");
                outFileHandle.Write("Date\t");
                foreach (BaseRegister item in internalModbusMap.getList())
                {
                    outFileHandle.Write(item.paramName);
                    outFileHandle.Write('\t');
                }
                outFileHandle.WriteLine();
            }
            dataGridView1.Update();
            dataGridView1.Refresh();
            
        }

        /// <summary>
        /// turns on timer maker sure timer runs continously
        /// </summary>
        public void updateGrid(){

            continuous = true;
            updateModbusData();
            update.Enabled = true;
            //update.Start();
        }
        /// <summary>
        /// A function to automatically scroll to the parameter a user is searching
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void textBox1_TextChanged(object sender, EventArgs e)
        {
            for (int i = 0; i < paramNames.Count; i++)
            {
                if (((string) dataGridView1[0,i].Value) == textBox1.Text)
                {
                    dataGridView1.FirstDisplayedScrollingRowIndex = i;
                }
            }
        }

        private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
        {

        }

        private void Start_Click(object sender, EventArgs e)
        {
            update.Start();
            continuous = true;
        }

        private void Stop_Click(object sender, EventArgs e)
        {
            update.Stop();
            continuous = false;
        }

        private void Refresh_Click(object sender, EventArgs e)
        {
            updateModbusData();
        }

    }
}
